package fr.xgouchet.wedding.client.presenter;

import java.util.ArrayList;
import java.util.Iterator;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;

import fr.xgouchet.wedding.client.event.FeedbackMessageEvent;
import fr.xgouchet.wedding.client.i18n.Messages;
import fr.xgouchet.wedding.client.service.TasksService;
import fr.xgouchet.wedding.client.view.ConfirmDialogBox;
import fr.xgouchet.wedding.shared.model.Task;

/**
 * Presenter for the agenda (Tasks list)
 * 
 * @author x.gouchet
 * 
 */
public class AgendaPresenter implements Presenter {
	/**
	 * Description of a Dashboard Display widget
	 */
	public interface Display {

		/**
		 * @return the Add button
		 */
		public HasClickHandlers getAddButton();

		/**
		 * @return the Delete button
		 */
		public HasClickHandlers getDeleteButton();

		/**
		 * @param event
		 *            a click event caught by the presenter
		 * @return the row corresponding to the click event
		 */
		public int getClickedRow(ClickEvent event);

		/**
		 * @return the selected rows
		 */
		public ArrayList<Integer> getSelectedRows();

		/**
		 * @return the category filters list
		 */
		public HasChangeHandlers getCategoryFilters();

		/**
		 * @return the complete filters list
		 */
		public HasChangeHandlers getCompleteFilters();

		/**
		 * @return the category filters list current value
		 */
		public String getCategoryFilterValue();

		/**
		 * @return the complete filters list current value
		 */
		public String getCompleteFilterValue();

		/**
		 * @param filters
		 *            the available filters
		 */
		public void setFilters(ArrayList<String> filters);

		/**
		 * clears the displayed data
		 */
		public void clearData();

		/**
		 * @param data
		 *            the data to display
		 * @return the edit button
		 */
		public HasClickHandlers addData(Task data);

		/**
		 * @return The display as a Widget object
		 */
		public Widget asWidget();
	}

	/**
	 * Description of a Guest Form widget
	 */
	public interface FormDisplay {

		/**
		 * @return the Save button
		 */
		public HasClickHandlers getSaveButton();

		/**
		 * @return the Cancel button
		 */
		public HasClickHandlers getCancelButton();

		/**
		 * @param t
		 *            the task to display
		 */
		public void displayTask(Task t);

		/**
		 * @return the task data
		 */
		public Task getTask();

		/**
		 * Starts the form
		 */
		public void startForm();

		/**
		 * Stops the form
		 */
		public void stopForm();

		/**
		 * @return The display as a Widget object
		 */
		public Widget asWidget();
	}

	/**
	 * Constructor
	 * 
	 * @param eventBus
	 *            the event bus
	 * @param display
	 *            the binded display to this presenter
	 * @param formDisplay
	 *            the binded form display to this presenter
	 */
	public AgendaPresenter(HandlerManager eventBus, Display display,
			FormDisplay formDisplay) {
		mEventBus = eventBus;
		mDisplay = display;
		mFormDisplay = formDisplay;
		mCategoryFilter = "";
		bind();
	}

	/**
	 * @see Presenter#go(HasWidgets)
	 */
	public void go(HasWidgets container) {
		container.clear();
		container.add(mDisplay.asWidget());
		fetchTasksDetails();
		mDisplay.setFilters(TasksService.getFilters());
	}

	/**
	 * Binds the presenter's widget to the event bus
	 */
	private void bind() {
		mDisplay.getAddButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				doAddTask();
			}
		});

		mDisplay.getDeleteButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				doWarnAndDelete();
			}
		});

		mDisplay.getCategoryFilters().addChangeHandler(new ChangeHandler() {
			public void onChange(ChangeEvent event) {
				mCategoryFilter = mDisplay.getCategoryFilterValue();
				fetchTasksDetails();
			}
		});

		mDisplay.getCompleteFilters().addChangeHandler(new ChangeHandler() {
			public void onChange(ChangeEvent event) {
				mCompleteFilter = mDisplay.getCompleteFilterValue();
				fetchTasksDetails();
			}
		});

		// mDisplay.getList().addClickHandler(new ClickHandler() {
		// public void onClick(ClickEvent event) {
		// int selectedRow = mDisplay.getClickedRow(event);
		// if (selectedRow >= 0) {
		// doEditTask(mTasks.get(selectedRow));
		// }
		// }
		// });

		mFormDisplay.getSaveButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				doCheckAndSave();
			}
		});

		mFormDisplay.getCancelButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				mFormDisplay.stopForm();
			}
		});
	}

	/**
	 * Fetches the guests details and present them in the view
	 */
	private void fetchTasksDetails() {
		TasksService.getTasksList(mCategoryFilter, mCompleteFilter,
				new AsyncCallback<ArrayList<Task>>() {
					public void onSuccess(ArrayList<Task> result) {
						mTasks = result;
						presentData();
					}

					public void onFailure(Throwable caught) {
						mEventBus.fireEvent(new FeedbackMessageEvent(caught
								.getMessage(), true));
					}
				});
	}

	/**
	 * Presents the data in the display
	 */
	private void presentData() {
		mDisplay.clearData();
		Iterator<Task> it = mTasks.iterator();
		while (it.hasNext()) {
			final Task task = it.next();
			HasClickHandlers edit = mDisplay.addData(task);
			edit.addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					doEditTask(task);
				}
			});
		}
	}

	/**
	 * Opens the Task Form
	 */
	private void doAddTask() {
		mFormDisplay.displayTask(new Task());
		mFormDisplay.startForm();
	}

	/**
	 * Opens the Task Form
	 * 
	 * @param t
	 *            the task to edit
	 */
	private void doEditTask(Task t) {
		mFormDisplay.displayTask(t);
		mFormDisplay.startForm();
	}

	/**
	 * Warns the user and delete if user confirms
	 */
	private void doWarnAndDelete() {
		ArrayList<Integer> rows = mDisplay.getSelectedRows();
		Iterator<Integer> it = rows.iterator();
		final ArrayList<String> list = new ArrayList<String>();

		while (it.hasNext()) {
			list.add(mTasks.get(it.next()).getID());
		}

		if (list.size() == 0)
			return;

		final ConfirmDialogBox box = new ConfirmDialogBox(Messages.I18N
				.confirmDeleteCaption(), Messages.I18N.confirmDeleteMessage(""));

		box.getYesButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				box.hide(true);

				TasksService.deleteTasks(list, new AsyncCallback<Object>() {
					public void onSuccess(Object result) {
						mEventBus.fireEvent(new FeedbackMessageEvent(
								Messages.I18N.deleteSuccessful(), true));
						fetchTasksDetails();
					}

					public void onFailure(Throwable caught) {
						mEventBus.fireEvent(new FeedbackMessageEvent(caught
								.getMessage(), true));
					}
				});
			}
		});

		box.getNoButton().addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				box.hide(true);
			}
		});
	}

	/**
	 * Checks the form data and save if the data is valid
	 */
	private void doCheckAndSave() {
		Task task = mFormDisplay.getTask();
		String msg = "";
		boolean valid = true;

		// TODO check task data before saving

		if (!valid) {
			mEventBus.fireEvent(new FeedbackMessageEvent(msg, true));
			return;
		}

		mFormDisplay.stopForm();
		TasksService.saveTask(task, new AsyncCallback<Object>() {
			public void onSuccess(Object result) {
				fetchTasksDetails();
			}

			public void onFailure(Throwable caught) {
				mEventBus.fireEvent(new FeedbackMessageEvent(caught
						.getMessage(), true));
			}
		});
	}

	/**
	 * Event Bus used to handle all communication between different parts of the
	 * module
	 */
	private final HandlerManager mEventBus;

	/**
	 * The linked display (widget)
	 */
	private Display mDisplay;

	/**
	 * The linked form display
	 */
	private FormDisplay mFormDisplay;

	/** The list of presented tasks */
	private ArrayList<Task> mTasks;

	/** Filter the tasks shown */
	private String mCategoryFilter;

	/** ignore complete tasks? */
	private String mCompleteFilter;

}
